package com.naza.rpc.circuitBreaker;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * 熔断器状态 - 半开
 *
 * @author yl
 */
final class HalfOpenState extends CircuitBreakerState {
    private Logger logger = LoggerFactory.getLogger(HalfOpenState.class);
    private CircuitBreakerMetrics circuitBreakerMetrics;
    private final float failureRateThreshold;
    private final int maxConcurrent;

    HalfOpenState(CircuitBreakerImpl circuitBreaker) {
        super(circuitBreaker);
        this.circuitBreakerMetrics = new CircuitBreakerMetrics(
                circuitBreaker.getCircuitBreakerConfig().getRingBufferSizeInHalfOpenState());
        this.failureRateThreshold = circuitBreaker.getCircuitBreakerConfig().getFailureRateThresholdInHalfOpenState();
        this.maxConcurrent = circuitBreaker.getCircuitBreakerConfig().getMaxConcurrentExecutionsInHalfOpenState();
    }

    /**
     * 当熔断器的并发执行数 < max并发时，允许执行
     *
     * @return 是否允许.
     */
    @Override
    boolean isCallPermitted() {
        long current = circuitBreaker.getCurrentExecutions();
        boolean result = (current < this.maxConcurrent);
        if (result) {
            if (logger.isDebugEnabled()) {
                logger.debug(circuitBreaker + " is at half open, Current is "
                        + current + ",max is " + this.maxConcurrent);
            }
        }
        return result;
    }

    @Override
    void recordFailure() {
        // Thread-safe
        checkFailureRate(circuitBreakerMetrics.recordFailure());
    }

    @Override
    void recordSuccess() {
        // Thread-safe
        checkFailureRate(circuitBreakerMetrics.recordSuccess());
    }

    /**
     * Checks if the current failure rate is above or below the threshold.
     * If the failure rate is above the threshold, transition the state machine to OPEN state.
     * If the failure rate is below the threshold, transition the state machine to CLOSED state.
     *
     * @param currentFailureRate the current failure rate
     */
    private void checkFailureRate(float currentFailureRate) {
        if (currentFailureRate != -1) {
            if (currentFailureRate >= failureRateThreshold) {
                circuitBreaker.transition(CircuitBreaker.State.HALF_OPEN, CircuitBreaker.State.OPEN);
            } else {
                circuitBreaker.transition(CircuitBreaker.State.HALF_OPEN, CircuitBreaker.State.CLOSED);
            }
        }
    }

    /**
     * Get the state of the CircuitBreaker
     */
    @Override
    CircuitBreaker.State getState() {
        return CircuitBreaker.State.HALF_OPEN;
    }
}
